001 /*
002 * Copyright 2004-2005 Stephen McConnell.
003 * Copyright 2004-2005 Niclas Hedhman.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.
015 *
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 package net.dpml.transit;
021
022 import java.io.File;
023 import java.io.FileOutputStream;
024 import java.io.IOException;
025 import java.io.OutputStreamWriter;
026 import java.io.PrintWriter;
027
028 import net.dpml.transit.link.LinkManager;
029 import net.dpml.transit.monitor.LoggingAdapter;
030 import net.dpml.transit.monitor.RepositoryMonitorRouter;
031 import net.dpml.transit.monitor.CacheMonitorRouter;
032 import net.dpml.transit.monitor.NetworkMonitorRouter;
033 import net.dpml.transit.model.TransitModel;
034
035 import net.dpml.lang.UnknownKeyException;
036 import net.dpml.util.Logger;
037
038 /**
039 * The Transit class manages the establishment of a singleton transit instance
040 * together with a service supporting the deployment of a application plugin and
041 * access to transit monitor routers.
042 *
043 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
044 * @version 1.0.1
045 */
046 public final class Transit
047 {
048 //------------------------------------------------------------------
049 // static
050 //------------------------------------------------------------------
051
052 /**
053 * DPML home key.
054 */
055 public static final String HOME_KEY = "dpml.home";
056
057 /**
058 * DPML home key.
059 */
060 public static final String DATA_KEY = "dpml.data";
061
062 /**
063 * DPML home key.
064 */
065 public static final String PREFS_KEY = "dpml.prefs";
066
067 /**
068 * Transit system key.
069 */
070 public static final String SYSTEM_KEY = "dpml.system";
071
072 /**
073 * Transit share key (alias to dpml.system).
074 */
075 public static final String SHARE_KEY = "dpml.share";
076
077 /**
078 * DPML environment variable string.
079 */
080 public static final String HOME_SYMBOL = "DPML_HOME";
081
082 /**
083 * DPML environment variable string.
084 */
085 public static final String SYSTEM_SYMBOL = "DPML_SYSTEM";
086
087 /**
088 * The DPML home directory established via assesment of the the ${dpml.home}
089 * system property and the DPML_HOME environment variable.
090 */
091 public static final File DPML_HOME;
092
093 /**
094 * If a system property named "dpml.system" is defined then the value
095 * is assigned otherwise the implementation will look for an environment
096 * variable named "DPML_SYSTEM".
097 */
098 public static final File DPML_SYSTEM;
099
100 /**
101 * The Transit personal data directory. The location of this diectory is system
102 * dependent.
103 */
104 public static final File DPML_DATA;
105
106 /**
107 * The Transit personal preferences directory. The location of this diectory is system
108 * dependent.
109 */
110 public static final File DPML_PREFS;
111
112 /**
113 * The Transit system version.
114 */
115 public static final String VERSION = "1.0.1";
116
117 static
118 {
119 System.setProperty( "java.protocol.handler.pkgs", "net.dpml.transit" );
120 System.setProperty( "dpml.transit.version", VERSION );
121
122 DPML_HOME = resolveHomeDirectory();
123 DPML_SYSTEM = resolveSystemDirectory( DPML_HOME );
124 DPML_DATA = resolveDataDirectory( DPML_HOME );
125 DPML_PREFS = resolvePreferencesDirectory( DPML_HOME );
126
127 System.setProperty( SYSTEM_KEY, DPML_SYSTEM.getAbsolutePath() );
128 System.setProperty( SHARE_KEY, DPML_SYSTEM.getAbsolutePath() );
129 System.setProperty( HOME_KEY, DPML_HOME.getAbsolutePath() );
130 System.setProperty( DATA_KEY, DPML_DATA.getAbsolutePath() );
131 System.setProperty( PREFS_KEY, DPML_PREFS.getAbsolutePath() );
132 }
133
134 /**
135 * Returns the singleton instance of the transit system. If Transit
136 * has not been initialized a the transit model will be resolved
137 * using the System property <tt>dpml.transit.profile</tt>.
138 * @return the singleton transit instance
139 * @exception TransitError if an error occurs during establishment
140 * @see DefaultTransitModel#getDefaultModel
141 */
142 public static Transit getInstance() throws TransitError
143 {
144 synchronized( Transit.class )
145 {
146 if( m_INSTANCE == null )
147 {
148 try
149 {
150 Logger logger = new LoggingAdapter( "dpml.transit" );
151 TransitModel model = DefaultTransitModel.getDefaultModel( logger );
152 return getInstance( model );
153 }
154 catch( Throwable e )
155 {
156 String message = e.getMessage();
157 Throwable cause = e.getCause();
158 throw new TransitError( message, cause );
159 }
160 }
161 else
162 {
163 return m_INSTANCE;
164 }
165 }
166 }
167
168 /**
169 * Returns the singleton instance of the transit system. If this method
170 * has already been invoked the server and monitor argument will be ignored.
171 *
172 * @param model the activate transit model
173 * @return the singleton transit instance
174 * @exception IOException if an error occurs during establishment
175 * @exception TransitAlreadyInitializedException if Transit is already initialized
176 */
177 public static Transit getInstance( TransitModel model )
178 throws IOException, TransitAlreadyInitializedException
179 {
180 synchronized( Transit.class )
181 {
182 if( m_INSTANCE == null )
183 {
184 m_INSTANCE = new Transit( model );
185
186 // before returning from this method we need to give the transit
187 // subsystems a chance to complete initialization actions that
188 // are themselves dependent on an establish Transit instance
189
190 m_INSTANCE.getTransitContext().initialize();
191 return m_INSTANCE;
192 }
193 else
194 {
195 final String error =
196 "Transit has already been initialized.";
197 throw new TransitAlreadyInitializedException( error );
198 }
199 }
200 }
201
202 //------------------------------------------------------------------
203 // state
204 //------------------------------------------------------------------
205
206 /**
207 * Singleton repository monitor router.
208 */
209 private RepositoryMonitorRouter m_repositoryMonitor;
210
211 /**
212 * Singleton cache monitor router.
213 */
214 private CacheMonitorRouter m_cacheMonitor;
215
216 /**
217 * Singleton network monitor router.
218 */
219 private NetworkMonitorRouter m_networkMonitor;
220
221 /**
222 * PrintWriter where operations troubleshooting messages
223 * can be written to.
224 */
225 private PrintWriter m_logWriter;
226
227 private SecuredTransitContext m_context;
228
229 /**
230 * Return the singleton transit content.
231 * @return the context instance
232 * @exception IllegalStateException if transit has not been initialized
233 */
234 SecuredTransitContext getTransitContext() throws IllegalStateException
235 {
236 if( null == m_context )
237 {
238 final String error =
239 "Transit context has not been initialized.";
240 throw new IllegalStateException( error );
241 }
242 else
243 {
244 return m_context;
245 }
246 }
247
248 /**
249 * Private constructor of a transit instance.
250 * @param model the active transit model
251 * @exception TransitException if an establishment error occurs
252 */
253 private Transit( TransitModel model ) throws TransitException
254 {
255 //
256 // create the transit context
257 //
258
259 try
260 {
261 m_context = SecuredTransitContext.create( model );
262 }
263 catch( TransitException e )
264 {
265 throw e;
266 }
267 catch( Throwable e )
268 {
269 final String error = "Unable to construct transit context.";
270 throw new TransitException( error, e );
271 }
272
273 //
274 // setup the monitors
275 //
276
277 m_repositoryMonitor = new RepositoryMonitorRouter();
278 m_cacheMonitor = new CacheMonitorRouter();
279 m_networkMonitor = new NetworkMonitorRouter();
280
281 try
282 {
283 // Setting up a temporary directory for Transit.
284
285 File temp = new File( DPML_DATA, "temp" );
286 temp.mkdirs();
287
288 // Setting up a permanent output troubleshooting resource
289 // for Transit.
290 File logs = new File( DPML_DATA, "logs" );
291 File logDir = new File( logs, "transit" );
292 logDir.mkdirs();
293 File logFile = new File( logDir, "transit.log" );
294 FileOutputStream fos = new FileOutputStream( logFile );
295 OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF-8" );
296 m_logWriter = new PrintWriter( osw, true );
297 }
298 catch( Throwable e )
299 {
300 final String error = "Unable to construct transit instance.";
301 throw new TransitException( error, e );
302 }
303 }
304
305 /**
306 * Return the current cache directory.
307 * @return the cache directory.
308 */
309 public File getCacheDirectory()
310 {
311 return getTransitContext().getCacheHandler().getCacheDirectory();
312 }
313
314 /**
315 * Return the link manager.
316 * @return the link manager
317 */
318 public LinkManager getLinkManager()
319 {
320 return getTransitContext().getLinkManager();
321 }
322
323 /**
324 * Return the cache layout.
325 * @return the layout
326 */
327 public Layout getCacheLayout()
328 {
329 return getTransitContext().getCacheLayout();
330 }
331
332 /**
333 * Return a layout object matching the supplied identifier.
334 * @param id the layout identifier
335 * @return the layout object
336 * @exception UnknownKeyException if the supplied layout id is unknown
337 * @exception IOException if an IO error occurs
338 */
339 public Layout getLayout( String id ) throws UnknownKeyException, IOException
340 {
341 return getTransitContext().getLayout( id );
342 }
343
344 /**
345 * Return the Transit repository service.
346 * @return the repository service
347 * @exception IllegalStateException if Transit has not been initialized
348 */
349 //public Repository getRepository() throws IllegalStateException
350 //{
351 // return getTransitContext().getRepository();
352 //}
353
354 /**
355 * Returns a reference to the repository monitor router. Client application
356 * may use the router to add, remove or replace existing monitors.
357 * @return the repository monitor router
358 */
359 public RepositoryMonitorRouter getRepositoryMonitorRouter()
360 {
361 return m_repositoryMonitor;
362 }
363
364 /**
365 * Returns a reference to the cache monitor router. Client application
366 * may use the router to add, remove or replace existing monitors.
367 * @return the cache monitor router
368 */
369 public CacheMonitorRouter getCacheMonitorRouter()
370 {
371 return m_cacheMonitor;
372 }
373
374 /**
375 * Returns a reference to the netowork monitor router. Client application
376 * may use the router to add, remove or replace existing monitors.
377 * @return the network monitor router
378 */
379 public NetworkMonitorRouter getNetworkMonitorRouter()
380 {
381 return m_networkMonitor;
382 }
383
384 /** Returns the LogWriter for the Transit system.
385 * This writer should only be used to report information that
386 * should not be output to the user in the course of normal
387 * execution but can aid to determine what has gone wrong in
388 * Transit, such as configuration problems, network problems
389 * and security issues.
390 * @return a PrintWriter where troubleshooting information can
391 * be written to.
392 */
393 public PrintWriter getLogWriter()
394 {
395 return m_logWriter;
396 }
397
398 /**
399 * Resolve the DPML home directory using assesment of the the ${dpml.home}
400 * system property, the DPML_HOME environment variable. If DPML_HOME is
401 * not declared, the behaviour is platform specific. If the os is Windows,
402 * the value returned is equivalent to $APPDATA\DPML whereas Unix environment
403 * will return ${user.home}/.dpml. The value returned may be overriden by
404 * setting a 'dpml.home' system property.
405 *
406 * @return the DPML home directory
407 */
408 private static File resolveHomeDirectory()
409 {
410 String home = System.getProperty( HOME_KEY );
411 if( null != home )
412 {
413 return new File( home );
414 }
415 home = Environment.getEnvVariable( HOME_SYMBOL );
416 if( null != home )
417 {
418 return new File( home );
419 }
420 String os = System.getProperty( "os.name" ).toLowerCase();
421 if( os.indexOf( "win" ) >= 0 )
422 {
423 home = Environment.getEnvVariable( "APPDATA" );
424 File data = new File( home );
425 return new File( data, "DPML" );
426 }
427 else
428 {
429 File user = new File( System.getProperty( "user.home" ) );
430 return new File( user, ".dpml" );
431 }
432 }
433
434 /**
435 * Resolve the DPML system home directory. If a system property
436 * named "dpml.system" is defined then the value as a file is
437 * returned otherwise the implementation will look for an environment
438 * variable named "DPML_SYSTEM" which if defined will be
439 * returned as a file otherwise a value equivalent to
440 * <tt>${dpml.home}/share</tt> will be returned.
441 *
442 * @param dpmlHomeDir the default DPML_HOME value
443 * @return the transit system directory
444 */
445 private static File resolveSystemDirectory( File dpmlHomeDir )
446 {
447 String home = System.getProperty( SYSTEM_KEY );
448 if( null != home )
449 {
450 return new File( home );
451 }
452 home = Environment.getEnvVariable( SYSTEM_SYMBOL );
453 if( null != home )
454 {
455 return new File( home );
456 }
457 else
458 {
459 return new File( dpmlHomeDir, "share" );
460 }
461 }
462
463 /**
464 * Resolve the DPML data directory. The value
465 * returned may be overriden by setting a 'dpml.data'
466 * system property otherwise the default value returned
467 * will be equivalent to <tt>${dpml.home}/data</tt>.
468 *
469 * @param dir the default DPML_HOME value
470 * @return the transit personal data directory
471 */
472 private static File resolveDataDirectory( File dir )
473 {
474 String path = System.getProperty( DATA_KEY );
475 if( null != path )
476 {
477 return new File( path );
478 }
479 else
480 {
481 return new File( dir, "data" );
482 }
483 }
484
485 /**
486 * Resolve the DPML prefs directory. The value
487 * returned may be overriden by setting a 'dpml.prefs'
488 * system property otherwise the default value returned
489 * will be equivalent to <tt>${dpml.home}/prefs</tt>.
490 *
491 * @param dir the default DPML_HOME value
492 * @return the transit personal data directory
493 */
494 private static File resolvePreferencesDirectory( File dir )
495 {
496 String path = System.getProperty( PREFS_KEY );
497 if( null != path )
498 {
499 return new File( path );
500 }
501 else
502 {
503 return new File( dir, "prefs" );
504 }
505 }
506
507 //------------------------------------------------------------------
508 // static internal
509 //------------------------------------------------------------------
510
511 /**
512 * Singleton transit instance.
513 */
514 private static Transit m_INSTANCE;
515
516 }